---------------------------------------
--              AmBList              --
---------------------------------------
-- Shows horizontal rows of content
local List = {
   Init = function(self)
      self._Data = { } -- Data, as a table of tuples containing size, absolute y position, control, userdata and index.
      self._OnCreate = nil -- Callback to create an item. Called with item index.
      self._OnDestroy = nil -- Callback to create an item. Called with item index.
      self._Scroll = 0 -- Absolute y pos of the top of the log
      self._StartIndex = 1 -- Index where controls start being shown.
      self._EndIndex = 1 -- Index where controls are no longer shown. this is excluded.

      local layout = vgui.Create("AmBContentSplitter", self)
      layout:SetDocking(true)
      layout:SetSplitters(true, { 100 })
      self._Layout = layout  -- Panel used for layout
      
      local datapanel = vgui.Create("AmBContentPanel",  layout:GetArea(1))
      datapanel:SetDocking(true)
      self._DataPanel = datapanel -- Panel that shows controls
		
		local fx = vgui.Create("AmBEffectsPanel", layout:GetArea(2))
		fx:SetDocking(true)
		fx:SetMoveFactor(300)
		self._FX = fx

      local scroll = vgui.Create("AmBScrollBar", fx)
      scroll:SetDocking(true)
      self._ScrollPanel = scroll -- Panel used for scrolling
		
		function layout:OnAreaResize(Index, NewSize, FullSize)
			if Index == 2 then
				layout:SetSplitter(1, FullSize - NewSize)
				return true
			end
			return false
		end

      scroll.OnValueChanged = function(NewValue)
         self._Scroll = NewValue
      end
      scroll:SetScrollValue(1.0, 1.0)
      
      self._LowestInvalidItem = 1 -- Lowest index that points to an item with incorrect absolute y position.
   end,

   -- Inserts an item into the log with the item size and index. Returns the item index.
   InsertItem = function(self, Size, UserData)
      local newindex = #(self._Data) + 1
      table.insert(self._Data, {Size, 0, nil, UserData, newindex})
      self._LowestInvalidItem = math.min(self._LowestInvalidItem, newindex)
      self:_RecalculateItemPos()
      return newindex
   end,

   -- Resizes the item at the index.
   ResizeItem = function(self, Index, NewSize)
      local item = self._Data[Index]
      if item[1] != NewSize then
         item[1] = NewSize
         self._LowestInvalidItem = math.min(self._LowestInvalidItem, Index)
         self:_RecalculateItemPos()
      end
   end,

   -- Sets the callbacks used to create and destroy items. Note that
   -- each callback provides three arguments(the index of the item
   -- created/destroyed, the content panel for the item and the userdata of the item). 
   -- If OnDestroy returns true or anything evaluating to it, the item will not be destroyed.
   SetCallbacks = function(self, OnCreate, OnDestroy)
      self._OnCreate = OnCreate
      self._OnDestroy = OnDestroy
   end,

   -- Sets userdata of an item. This data is linked to the item and used with
   -- callbacks.
   SetItemUserData = function(self, Index, Value)
      self._Data[Index][4] = Value
   end,

   -- Sets the position of the top of the log. The higher this is, the more recent
   -- the log entries.
   SetScrollValue = function(self, Value)
      self._Scroll = math.max(math.min(Value, self:GetFullHeight() - self._DataPanel:GetTall()), 0)
      self:_RecalculateShownItem()
      self:_UpdateControls()
   end,

   GetScrollValue = function(self)
      return self._Scroll
   end,
	
	-- Scrolls to show the last items in the list.
	ScrollDown = function(self)
		self:SetScrollValue(1.0 / 0.0)
	end,

   -- Called when the actual height of the log is changed.
   OnFullHeightChanged = function(Value)
   end,

   -- Gets the full height of the items in the log.
   GetFullHeight = function(self)
      if #self._Data < 1 then
         return 0
      end
      local item = self._Data[#self._Data]
      return item[1] + item[2]
   end,
	
	-- Gets the effects panel for the scroll bar
	GetScrollFX = function(self)
		return self._FX
	end,
   
   PanelThink = function(self)
      self:_RecalculateItemPos()
      self:_RecalculateShownItem()
      self:_UpdateControls()
   end,
	
	-- Calls a function for all visible list items. The functions
	-- arguments are, the index of the item, the control of the item, and
	-- the userdata for the item.
	ForEach = function(self, Function)
		for i = self._StartIndex, (self._EndIndex - 1) do
			local item = self._Data[i]
			Function(i, item[3], item[4])
		end
	end,

   -- Recalculates the items shown. These items are the ones created
   -- with the OnCreate callback and are removed with the OnDestroy
   -- callback. Items that has child controls are not removed.
   _RecalculateShownItem = function(self)
      local height = self._DataPanel:GetTall()
      local y = self._Scroll
      local startind = 1
      while startind < #(self._Data) do
         local item = self._Data[startind]
         if item[1] + item[2] >= y then
            break
         end
         startind = startind +1
      end
      local endind  = startind
      while endind < #(self._Data) do
         local item = self._Data[endind]
         if item[2] > y + height then
            break
         end
         endind = endind +1
      end
      endind = endind + 1
      if endind > #self._Data + 1 or startind > #self._Data then
         startind = 1
         endind = 1
      end
      self:_ModifyItems(startind, endind)
   end,

   -- Modifies the items shown by creating and destroying them.
   _ModifyItems = function(self, NewStartIndex, NewEndIndex)
      local nsi = NewStartIndex
      local nei = NewEndIndex
      local csi = self._StartIndex
      local cei = self._EndIndex
      if nsi != csi or nei != cei then
         local si = math.min(nsi, csi) -- Gets bounds set by past and current indices
         local ei = math.max(nei, cei)
         for x = si, (ei - 1) do
            local item = self._Data[x]
            local control = item[3]
            local inrange = (nsi <= x and x < nei)
            if control == nil and inrange then
               control = vgui.Create("AmBContentPanel", self._DataPanel)
               item[3] = control

               --Resizing
               control.OnChildRequestResize = function(control, NewWidth, NewHeight)
                  if NewWidth != self._DataPanel:GetWide() then
                     self._DataPanel:RequestResize(NewWidth, self._DataPanel:GetTall())
                  end
						if item[1] != NewHeight then
							self:ResizeItem(item[5], NewHeight)
						end
                  return true
               end

               if self._OnCreate != nil then
                  self._OnCreate(x, control, item[4])
               end
            end
            if control != nil and not inrange then
               if self._OnDestroy != nil then
                  if not self._OnDestroy(x, control, item[4]) then
                     item[3]:Remove()
                     item[3] = nil
                  end
               end
            end
         end
         self:_UpdateControls()
         self._StartIndex = nsi
         self._EndIndex = nei
      end
   end,

   -- Positions and resizes all controls
   _UpdateControls = function(self)
      for x = self._StartIndex, (self._EndIndex - 1) do
         local item = self._Data[x]
         local control = item[3]
         if control != nil then
            control:SetSize(self._DataPanel:GetWide(), item[1])
            control:SetPos(0, item[2] - self._Scroll)
         end
      end
   end,

   -- Recalculates the position of items starting at the lowest invalid index.
   _RecalculateItemPos = function(self)
      local index = self._LowestInvalidItem
      while index <= #(self._Data) do
         local cur = self._Data[index]
         if index == 1 then
            cur[2] = 0
         else
            local prev = self._Data[index - 1]
            cur[2] = prev[1]  + prev[2]
         end
         index = index + 1
      end
      local newfullheight = self:GetFullHeight()
      local scrollsize, fullsize = self._ScrollPanel:GetScrollValue()
      local scrollpos = self._ScrollPanel:GetScrollPosition()
      local cursize = self._DataPanel:GetTall()
      self._ScrollPanel:SetScrollValue(cursize, newfullheight)
      if scrollpos >= fullsize - scrollsize then
         self._ScrollPanel:SetScrollPosition(newfullheight - cursize)
		else
			self._ScrollPanel:SetScrollPosition(self._Scroll)
      end
      self.OnFullHeightChanged(newfullheight)
      self._LowestInvalidItem = (#self._Data) + 1
   end
}
AmB.UI.List = List
vgui.Register("AmBList", List, "AmBContentPanel")